package md.mru.core.service;

import md.mru.core.common.BaseDtoMapper;
import md.mru.core.common.BaseDtoModelMapper;
import md.mru.core.dao.TreeModelDao;
import md.mru.core.domains.administration.*;
import md.mru.core.dtos.*;
import md.mru.core.repositories.*;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author sergiu.marin
 * @since 15.04.2014.
 */
@Service
public class TreeService {

    @Autowired
    @Qualifier(value = "dtoMapper")
    private BaseDtoMapper mapper;

    @Autowired
    private BaseDtoModelMapper dtoModelMapper;

    @Autowired
    private TreeModelDao treeModelDao;
    @Autowired
    private TreeNodeModelRepository treeNodeModelRepository;
    @Autowired
    private TreeModelUserGroupRepository treeModelUserGroupRepository;
    @Autowired
    private TreeModelFolderRepository treeModelFolderRepository;
    @Autowired
    private FunctionRepository functionRepository;
    @Autowired
    private UserGroupRepository userGroupRepository;

    @Deprecated
    public RootTreeModelDto getRootView() {
        return treeModelDao.getRootView();
    }

    public List<TreeNodeTypeDto> getTreeTypes() {
        List<TreeNodeType> functions = functionRepository.getFunctions();
        return dtoModelMapper.mapList(functions, TreeNodeTypeDto.class);
    }

    public long saveTreeNode(TreeNodeModelDto node/*, TreeNodeModelDto currentNode*/) {
        TreeNodeModel nodeModel = mapper.map(node, TreeNodeModel.class);
        //nodeModel.setParentId(currentNode.getParentId());
        //nodeModel.setId(0);
        return treeNodeModelRepository.save(nodeModel).getId();
    }

    @Transactional
    public void update(TreeNodeModelDto node) {
        treeNodeModelRepository.update(node.getId(), node.getName(), node.getDescription());
    }

    public TreeNodeModelDto getTreeNode(long treeNodeId) {
        TreeNodeModel nodeModel = treeNodeModelRepository.findOne(treeNodeId);
        return mapper.map(nodeModel, TreeNodeModelDto.class);
    }

    public List<TreeModelUserGroupDto> loadTreeGroupModel(Long groupId) {
        List<TreeModelUserGroup> treeModelUserGroups = treeModelUserGroupRepository.getTreeModelUserGroups(groupId);
        return mapper.mapList(treeModelUserGroups, TreeModelUserGroupDto.class);
    }

    public RootTreeModelDto getGroupRootView(Long groupId) {
        List<TreeModelUserGroup> treeUserGroups = treeModelDao.getNodesForGrp(groupId);
        Set<Long> groupDisplayNodes = new HashSet<Long>();
        Set<Long> groupCanWriteNodes = new HashSet<Long>();
        /*for (TreeModelUserGroup ng : treeModelUserGroups) {
            groupDisplayNodes.add(ng.getId().getTreeNodeModel().getId());
            if (ng.getCanWrite()) {
                groupCanWriteNodes.add(ng.getId().getTreeNodeModel().getId());
            }
        }*/
        RootTreeModelDto rootView = treeModelDao.getGroupRootView(groupDisplayNodes);
        rootView.setCanWriteNodes(groupCanWriteNodes);
        return rootView;
    }

    public void saveRoleTreeNodes(TreeNode[] selectedTreeNodes, Set<Long> canWriteNodeIds, long groupId) {
        treeModelDao.saveRoleTreeNodes(selectedTreeNodes, canWriteNodeIds, groupId);
        this.treeModelUserGroupRepository.deleteByGroupId(groupId);
    }

    @Transactional
    public void saveRoleTreeNodes(List<TreeNodeModelDto> treeNodeModelDtos, Set<Long> canWriteNodeIds, long groupId) {
        List<TreeNodeModel> treeNodeModels = mapper.mapList(treeNodeModelDtos, TreeNodeModel.class);
        UserGroup group = this.userGroupRepository.findOne(groupId);
        List<TreeModelUserGroup> treeModelUserGroups = new ArrayList<>(treeNodeModels.size());
        for (TreeNodeModel treeNodeModel : treeNodeModels) {
            treeModelUserGroups.add(new TreeModelUserGroup(treeNodeModel, group, canWriteNodeIds.contains(treeNodeModel.getId())));
        }
        this.treeModelUserGroupRepository.deleteByGroupId(groupId);
        this.treeModelUserGroupRepository.save(treeModelUserGroups);
    }

    @Transactional
    public void updateTreeNodesPosition(List<TreeNodeModelDto> nodes) {
        List<TreeNodeModel> nodeModels = mapper.mapList(nodes, TreeNodeModel.class);
        for (TreeNodeModel t : nodeModels) {
            treeNodeModelRepository.savePosition(t.getPosition(), t.getId());
        }
    }

    public List<TreeNodeModelDto> loadTreeNodeModels() {
        List<TreeNodeModel> treeNodeModels = treeNodeModelRepository.loadTreeNodeModels();
        return mapper.mapList(treeNodeModels, TreeNodeModelDto.class);
    }

    public List<TreeNodeModelDto> loadTreeNodeModels(long userGroupId) {
        List<TreeNodeModel> treeNodeModels = treeNodeModelRepository.loadTreeNodeModels(userGroupId);
        return mapper.mapList(treeNodeModels, TreeNodeModelDto.class);
    }

    public void saveTreeFolder(TreeFolderDto treeFolderDto) {
        TreeFolder treeFolder = mapper.map(treeFolderDto, TreeFolder.class);
        treeFolder.setValidFrom(new Date());
        treeFolder.setValidTo(new Date());
        treeModelFolderRepository.save(treeFolder);
    }

    public void updateTreeFolder(long treeId, long folderId, boolean active) {
        treeModelFolderRepository.updateTreeFolder(treeId, folderId, active);
    }

    public void removeTreeFolderLink(TreeFolderDto nodeTreeFolderDto) {
        TreeFolder treeFolder = mapper.map(nodeTreeFolderDto, TreeFolder.class);
        treeModelFolderRepository.delete(treeFolder);
    }

    public void saveTreeNodeGroup(TreeModelUserGroupDto treeNodeGroupDto) {
        TreeModelUserGroup treeModelUserGroup = mapper.map(treeNodeGroupDto, TreeModelUserGroup.class);
        treeModelUserGroupRepository.save(treeModelUserGroup);
    }
}
